package org.rc.dao;

import com.googlecode.objectify.Query;
import com.googlecode.objectify.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.rc.model.Athlete;
import org.rc.model.Race;
import org.rc.model.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author dokeeffe
 */
public class AthleteDao extends GenericDao<Athlete> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AthleteDao.class);

    /**
     * Constructor
     * @param persistentClass 
     */
    public AthleteDao() {
        super(Athlete.class);
    }

    /**
     * Gets he intersection set of Athletes for all races passed
     * @param races
     * @return 
     */
    public Map<Key<Athlete>, Athlete> getAthletesThatCompetedinAll(List<Race> races) {
        String cacheKey = generateCacheKey(races);
        Map<Key<Athlete>, Athlete> result = (Map<Key<Athlete>, Athlete>) genericCache.get(cacheKey);
        if (result == null) {
            Set<Key<Athlete>> intermediateResult = new HashSet<Key<Athlete>>();
            for (Race race : races) {
                Query<Result> q = ofy().query(Result.class);
                q.filter("race", race);
                Set<Key<Athlete>> athletes = new HashSet<Key<Athlete>>();
                for (Result r : q.fetch()) {
                    athletes.add(r.getAthlete());
                }
                if (intermediateResult.isEmpty()) {
                    intermediateResult.addAll(athletes);
                } else {
                    intermediateResult.retainAll(athletes);
                }
            }
            result = ofy().get(intermediateResult);
            genericCache.put(cacheKey, result);
        }
        return result;
    }

    /**
     * Gets he intersection set of Athletes for all races passed
     * @param races
     * @return 
     */
    public Map<Athlete, List<Result>> getResultsForCommonAthletesInAll(List<Race> races) {
        Map<Key<Athlete>, List<Result>> result = null;
        for (Race race : races) {
            Set<Key<Athlete>> intermediateAthletesA = new HashSet<Key<Athlete>>();
            Query<Result> q = ofy().query(Result.class);
            q.filter("race", race);
            if (result == null) {
                //first race in list, populate the result with all
                result = new HashMap<Key<Athlete>, List<Result>>();
                for (Result r : q.fetch()) {
                    List<Result> athletesResults = new ArrayList<Result>();
                    athletesResults.add(r);
                    result.put(r.getAthlete(), athletesResults);
                }
            } else {
                //not the first race in list. Get the intersection.
                for (Result r : q.fetch()) {
                    if(result.containsKey(r.getAthlete())) {
                        result.get(r.getAthlete()).add(r);
                    }
                    intermediateAthletesA.add(r.getAthlete());
                }
                intermediateAthletesA.retainAll(result.keySet()); //intermediateAthletesA now contains the intersection of 2 sets. Keep only these
                Map<Key<Athlete>, List<Result>> newResult = new HashMap<Key<Athlete>, List<Result>>();
                for(Key<Athlete> ath : intermediateAthletesA) {
                    newResult.put(ath, result.get(ath));
                }
                result = newResult;
            }
        }
        //query datastore to get Athletes for Keys
        Map<Key<Athlete>, Athlete> athletes = ofy().get(result.keySet());
        Map<Athlete, List<Result>> finalResult = new HashMap<Athlete, List<Result>>();
        for(Key<Athlete> ak : result.keySet()) {
            finalResult.put(athletes.get(ak), result.get(ak));
        }
        return finalResult;
    }

    /**
     * Generate a cache key string
     * @param races
     * @return 
     */
    private String generateCacheKey(List<Race> races) {
        StringBuilder sb = new StringBuilder();
        for (Race r : races) {
            sb.append(r.getId());
        }
        return "athletesInRaces" + sb.toString();
    }
}
